#include <iostream>
#include <fstream>
#include <string>
#include <cstring>
#include <cstdlib>
#include <pthread.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>

#define SIZE 1024 * 10

void usage(std::string proc)
{
    std::cout << "usage:\n\t" << proc << "need port" << std::endl;
}

// 响应一个http正文的文本内容回去
// void* handle_http_request(void* args)
// {
//     //http协议,如果我们自己写的话,其本质就是,我们根据协议内容,进行文本分析
//     //所谓的文本分析
//     //1.http协议本身的字段
//     //2.如果有的话,可以提取参数

//     //GET 或 POST 其实是前后端交互的一个重要方式

//     pthread_detach(pthread_self());
//     int sock = *(int*)args;
//     delete (int*)args;
//     args = nullptr;

//     char buffer[SIZE];
//     memset(buffer, 0, sizeof(buffer));

//     //这种读法是不正确的,只不过现在没有暴露出来
//     ssize_t ret = recv(sock, buffer, sizeof(buffer) - 1, 0);
//     if (ret > 0)
//     {
//         buffer[ret] = '\0';
//         std::cout << buffer; // 查看http的请求格式

//         std::string http_response = "http/1.0 200 OK\n";
//         //text/plain代表正文是普通文件
//         http_response += "Content-Type: text/plain\n";
//         //代表的是空行
//         http_response += "\n";
//         //正文
//         http_response += "liukun233 yyds!";

//         send(sock, http_response.c_str(), http_response.size(), 0);
//     }

//     close(sock);
//     return nullptr;
// }

// 通过一次http请求,来拿到网站的首页
// 我们创建一个web_root的目录,我们设置为这个为web的根目录
// 那么我们在这个目录下放置的内容,都可以叫做资源
// web_root目录下的index.html就是网站的首页

#define WEB_ROOT "./web_root"
#define HOME_PAGE "/index.html"

void* handle_http_request(void* args)
{
    //http协议,如果我们自己写的话,其本质就是,我们根据协议内容,进行文本分析
    //所谓的文本分析
    //1.http协议本身的字段
    //2.如果有的话,可以提取参数

    //GET 或 POST 其实是前后端交互的一个重要方式

    pthread_detach(pthread_self());
    int sock = *(int*)args;
    delete (int*)args;
    args = nullptr;

    char buffer[SIZE];
    memset(buffer, 0, sizeof(buffer));

    ssize_t ret = recv(sock, buffer, sizeof(buffer) - 1, 0);
    std::cout << buffer << std::endl;
    if (ret >0)
    {
        std::string html_file = WEB_ROOT;
        html_file += HOME_PAGE;

        std::ifstream ifs(html_file);
        if (!ifs.is_open())
        {
            //文件访问失败
            std::string http_response = "http/1.1 404 NOT FOUND\n";
            std::cout << http_response << std::endl;
            //正文部分的数据类型

        }
        else
        {
            //文件访问成功
            //我们怎么知道这个文件的大小是多少呢?
            //所以我们需要使用一个stat的函数,这个函数可以帮助我们获取这个文件的信息
            //其中传入一个stat的结构体(输出型参数),把这个文件的信息都写入这个结构体中
            struct stat file_status;
            stat(html_file.c_str(), &file_status);

            //返回的时候,不仅仅是返回正文网页信息,而且还要包括http的响应
            std::string http_response = "http/1.1 200 ok\r\n";
            //正文部分的数据类型
            http_response += "Content-Type: text/html; charset=utf-8\r\n";
            http_response += "Content-Length: ";
            http_response += std::to_string(file_status.st_size);
            http_response += "\r\n";
            //这个是空行
            http_response += "\r\n";
            //接下来是正文
            std::string content;
            std::string line;
            while(std::getline(ifs, line))
            {
                content += line;
            }
            http_response += content;

            ifs.close();
            std::cout << http_response << std::endl;
            send(sock, http_response.c_str(), http_response.size(), 0);

        }
    }
    close(sock);
    return nullptr;
}

// 这里我们来验证http中的GET和POST方法
// #define WEB_ROOT "./web_root"
// #define HOME_PAGE "/index.html"

// void open_error(int sock)
// {
//     std::ifstream ifs("./web_root/error.html");
//     struct stat file_status;
//     stat("./web_root/error.html", &file_status);

//     // 文件访问失败
//     std::string http_response = "http/1.1 404 NOT FOUND\r\n";

//     // 正文部分的数据类型
//     http_response += "Content-Type: text/html; charset=utf8\r\n";
//     http_response += "Content-Length: ";
//     http_response += std::to_string(file_status.st_size);
//     http_response += "\r\n";
//     http_response += "\r\n";

//     std::string content;
//     std::string line;
//     while (std::getline(ifs, line))
//     {
//         content += line;
//     }
//     http_response += content;

//     ifs.close();
//     std::cout << http_response << std::endl;
//     send(sock, http_response.c_str(), http_response.size(), 0);
// }

// void open_home_page(int sock)
// {
//     std::string html_file = WEB_ROOT;
//     html_file += HOME_PAGE;

//     std::ifstream ifs(html_file.c_str());
//     struct stat file_status;
//     stat(html_file.c_str(), &file_status);

//     // 返回的时候,不仅仅是返回正文网页信息,而且还要包括http的响应
//     std::string http_response = "HTTP/1.1 200 ok\r\n";
//     // 正文部分的数据类型
//     http_response += "Content-Type: text/html; charset=utf-8\r\n";
//     http_response += "Content-Length: ";
//     http_response += std::to_string(file_status.st_size);
//     http_response += "\r\n";
//     // 这个是空行
//     http_response += "\r\n";
//     // 接下来是正文
//     std::string content;
//     std::string line;
//     while (std::getline(ifs, line))
//     {
//         content += line;
//     }
//     http_response += content;

//     ifs.close();
//     std::cout << http_response << std::endl;
//     send(sock, http_response.c_str(), http_response.size(), 0);
// }

// void *handle_http_request(void *args)
// {
//     // http协议,如果我们自己写的话,其本质就是,我们根据协议内容,进行文本分析
//     // 所谓的文本分析
//     // 1.http协议本身的字段
//     // 2.如果有的话,可以提取参数

//     // GET 或 POST 其实是前后端交互的一个重要方式

//     pthread_detach(pthread_self());
//     int sock = *(int *)args;
//     delete (int *)args;
//     args = nullptr;

//     char buffer[SIZE];
//     memset(buffer, 0, sizeof(buffer));

//     ssize_t ret = recv(sock, buffer, sizeof(buffer) - 1, 0);
//     std::cout << buffer << std::endl;
//     if (ret > 0)
//     {

//         std::string URL = buffer;
//         size_t pos_1 = URL.find('/');
//         size_t pos_2 = URL.find("HTTP");
//         std::string substr = URL.substr(pos_1, pos_2 - 5);

//         if (substr == "/")
//         {
//             open_home_page(sock);
//         }
//         else
//         {
//             std::string web_root = "./web_root";
//             web_root += substr;
//             substr = web_root;

//             std::cout << substr << std::endl;

//             std::ifstream ifs(substr.c_str());
//             if (!ifs.is_open())
//             {
//                 open_error(sock);
//             }
//             else
//             {
//                 // 文件访问成功
//                 // 我们怎么知道这个文件的大小是多少呢?
//                 // 所以我们需要使用一个stat的函数,这个函数可以帮助我们获取这个文件的信息
//                 // 其中传入一个stat的结构体(输出型参数),把这个文件的信息都写入这个结构体中
//                 struct stat file_status;
//                 stat(substr.c_str(), &file_status);

//                 // 返回的时候,不仅仅是返回正文网页信息,而且还要包括http的响应
//                 std::string http_response = "HTTP/1.1 200 ok\r\n";
//                 // 正文部分的数据类型
//                 http_response += "Content-Type: text/html; charset=utf-8\r\n";
//                 http_response += "Content-Length: ";
//                 http_response += std::to_string(file_status.st_size);
//                 http_response += "\r\n";
//                 // 这个是空行
//                 http_response += "\r\n";
//                 // 接下来是正文
//                 std::string content;
//                 std::string line;
//                 while (std::getline(ifs, line))
//                 {
//                     content += line;
//                 }
//                 http_response += content;

//                 ifs.close();
//                 std::cout << http_response << std::endl;
//                 send(sock, http_response.c_str(), http_response.size(), 0);
//             }
//         }
//     }
//     close(sock);
//     return nullptr;
// }


//这里我们来进行验证状态码3XX,也就是验证永久重定向和临时重定向
//同时我们设置cookie
// void open_error(int sock)
// {
//     std::ifstream ifs("./web_root/error.html");
//     struct stat file_status;
//     stat("./web_root/error.html", &file_status);

//     // 文件访问失败
//     std::string http_response = "http/1.1 404 NOT FOUND\r\n";

//     // 正文部分的数据类型
//     http_response += "Content-Type: text/html; charset=utf8\r\n";
//     http_response += "Content-Length: ";
//     http_response += std::to_string(file_status.st_size);
//     http_response += "\r\n";
//     http_response += "\r\n";

//     std::string content;
//     std::string line;
//     while (std::getline(ifs, line))
//     {
//         content += line;
//     }
//     http_response += content;

//     ifs.close();
//     std::cout << http_response << std::endl;
//     send(sock, http_response.c_str(), http_response.size(), 0);
// }

// void open_home_page(int sock)
// {
//     // 返回的时候,不仅仅是返回正文网页信息,而且还要包括http的响应
//     //永久重定向
//     //std::string http_response = "HTTP/1.1 301 Move Permanently\r\n";
//     //临时重定向
//     //std::string http_response = "HTTP/1.1 302 Moved Temporarily\r\n";
//     //临时重定向
//     std::string http_response = "HTTP/1.1 307 Moved Temporarily\r\n";
//     // 正文部分的数据类型
//     http_response += "Location: https://www.bilibili.com/\r\n";
//     // 这个是空行
//     http_response += "\r\n";

//     std::cout << http_response << std::endl;
//     send(sock, http_response.c_str(), http_response.size(), 0);
// }

// void *handle_http_request(void *args)
// {
//     // http协议,如果我们自己写的话,其本质就是,我们根据协议内容,进行文本分析
//     // 所谓的文本分析
//     // 1.http协议本身的字段
//     // 2.如果有的话,可以提取参数

//     // GET 或 POST 其实是前后端交互的一个重要方式

//     pthread_detach(pthread_self());
//     int sock = *(int *)args;
//     delete (int *)args;
//     args = nullptr;

//     char buffer[SIZE];
//     memset(buffer, 0, sizeof(buffer));

//     ssize_t ret = recv(sock, buffer, sizeof(buffer) - 1, 0);
//     std::cout << buffer << std::endl;
//     if (ret > 0)
//     {

//         std::string URL = buffer;
//         size_t pos_1 = URL.find('/');
//         size_t pos_2 = URL.find("HTTP");
//         std::string substr = URL.substr(pos_1, pos_2 - 5);

//         if (substr == "/")
//         {
//             open_home_page(sock);
//         }
//         else
//         {
//             std::string web_root = "./web_root";
//             web_root += substr;
//             substr = web_root;

//             std::cout << substr << std::endl;

//             std::ifstream ifs(substr.c_str());
//             if (!ifs.is_open())
//             {
//                 open_error(sock);
//             }
//             else
//             {
//                 // 文件访问成功
//                 // 我们怎么知道这个文件的大小是多少呢?
//                 // 所以我们需要使用一个stat的函数,这个函数可以帮助我们获取这个文件的信息
//                 // 其中传入一个stat的结构体(输出型参数),把这个文件的信息都写入这个结构体中
//                 struct stat file_status;
//                 stat(substr.c_str(), &file_status);

//                 // 返回的时候,不仅仅是返回正文网页信息,而且还要包括http的响应
//                 std::string http_response = "HTTP/1.1 200 ok\r\n";
//                 // 正文部分的数据类型
//                 http_response += "Content-Type: text/html; charset=utf-8\r\n";
//                 http_response += "Content-Length: ";
//                 http_response += std::to_string(file_status.st_size);
//                 http_response += "\r\n";
//                 http_response += "Set-Cookie: id = qingdiaoshen\r\n";
//                 http_response += "Set-Cookie: password = 123456\r\n";
//                 // 这个是空行
//                 http_response += "\r\n";
//                 // 接下来是正文
//                 std::string content;
//                 std::string line;
//                 while (std::getline(ifs, line))
//                 {
//                     content += line;
//                 }
//                 http_response += content;

//                 ifs.close();
//                 send(sock, http_response.c_str(), http_response.size(), 0);
//             }
//         }
//     }
//     close(sock);
//     return nullptr;
// }

int main(int argc, char *argv[])
{
    if (argc != 2)
    {
        usage(argv[0]);
        exit(1);
    }

    int listen_sock = socket(AF_INET, SOCK_STREAM, 0);
    if (listen_sock < 0)
    {
        perror("create sock");
        std::cout << "errno : " << errno << std::endl;
        exit(2);
    }

    uint16_t port = atoi(argv[1]);
    struct sockaddr_in local;
    memset(&local, 0, sizeof(local));
    local.sin_family = AF_INET;
    local.sin_port = htons(port);
    local.sin_addr.s_addr = INADDR_ANY;

    if (bind(listen_sock, (struct sockaddr *)&local, sizeof(local)) < 0)
    {
        perror("bind");
        std::cout << "errno : " << errno << std::endl;
        exit(3);
    }

    int back_log = 5;
    if (listen(listen_sock, back_log) < 0)
    {
        perror("listen");
        std::cout << "errno : " << errno << std::endl;
        exit(4);
    }

    while (true)
    {
        struct sockaddr_in peer;
        memset(&peer, 0, sizeof(peer));
        socklen_t len = sizeof(peer);

        int new_sock = accept(listen_sock, (struct sockaddr *)&peer, &len);
        if (new_sock >= 0)
        {
            uint16_t port = ntohs(peer.sin_port);
            std::string ip = inet_ntoa(peer.sin_addr);

            std::cout << "get a new link : [" << ip << ":" << port << "]# " << new_sock << std::endl;
        }
        else
        {
            continue;
        }

        pthread_t tid;
        int *parm = new int(new_sock);
        pthread_create(&tid, nullptr, handle_http_request, parm);
    }

    return 0;
}
